{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Qwen-1_8B-chat Intel CPU 部署实战"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 概述\n",
    "\n",
    "本文介绍了在 Intel 设备上部署 Qwen 1.8B 模型的过程，你需要至少16GB内存的机器来完成这项任务，我们将使用英特尔的大模型推理库 [BigDL](https://github.com/intel-analytics/BigDL) 来实现完整过程。\n",
    "\n",
    "Bigdl-llm 是一个在英特尔设备上运行 LLM（大语言模型）的加速库，通过 INT4/FP4/INT8/FP8 精度量化和架构针对性优化以实现大模型在 英特尔 CPU、GPU上的低资源占用与高速推理能力（适用于任何 PyTorch 模型）。\n",
    "\n",
    "本文演示为了通用性，只涉及 CPU 相关的代码，如果你想学习如何在 Intel GPU 上部署大模型，可以参考[官网文档](https://bigdl.readthedocs.io/en/latest/doc/LLM/Overview/install_gpu.html)。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 环境配置\n",
    "\n",
    "在开始之前，我们需要准备好 bigdl-llm 以及之后部署的相关运行环境，我们推荐你在 python 3.9 的环境中进行之后的操作。\n",
    "\n",
    "如果你发现下载速度过慢，可以尝试更换默认镜像源：`pip config set global.index-url https://pypi.doubanio.com/simple`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%pip install --pre --upgrade bigdl-llm[all] \n",
    "%pip install gradio \n",
    "%pip install hf-transfer\n",
    "%pip install transformers_stream_generator einops\n",
    "%pip install tiktoken"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 模型下载\n",
    "\n",
    "首先，我们通过 huggingface-cli 获取 qwen-1.8B 模型，耗时较长需要稍作等待；这里考虑到国内的下载限制增加了环境变量加速下载。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "\n",
    "# 设置环境变量\n",
    "os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'\n",
    "# 下载模型\n",
    "os.system('huggingface-cli download --resume-download qwen/Qwen-1_8B-Chat --local-dir qwen18chat_src')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 保存量化模型\n",
    "\n",
    "为了实现大语言模型的低资源消耗推理，我们首先需要把模型量化到 int4 精度，随后序列化保存在本地的相应文件夹方便重复加载推理；利用 `save_low_bit` api 我们可以很容易实现这一步。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bigdl.llm.transformers import AutoModelForCausalLM\n",
    "from transformers import  AutoTokenizer\n",
    "import os\n",
    "if __name__ == '__main__':\n",
    "    model_path = os.path.join(os.getcwd(),\"qwen18chat_src\")\n",
    "    model = AutoModelForCausalLM.from_pretrained(model_path, load_in_low_bit='sym_int4', trust_remote_code=True)\n",
    "    tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)\n",
    "    model.save_low_bit('qwen18chat_int4')\n",
    "    tokenizer.save_pretrained('qwen18chat_int4')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 加载量化模型\n",
    "\n",
    "保存 int4 模型文件后，我们便可以把他加载到内存进行进一步推理；如果你在本机上无法导出量化模型，也可以在更大内存的机器中保存模型再转移到小内存的端侧设备中运行，大部分常用家用PC即可满足 int4 模型实际运行的资源需求。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import time\n",
    "from bigdl.llm.transformers import AutoModelForCausalLM\n",
    "from transformers import AutoTokenizer\n",
    "\n",
    "QWEN_PROMPT_FORMAT = \"<human>{prompt} <bot>\"\n",
    "load_path = \"qwen18chat_int4\"\n",
    "model = AutoModelForCausalLM.load_low_bit(load_path, trust_remote_code=True)\n",
    "tokenizer = AutoTokenizer.from_pretrained(load_path, trust_remote_code=True)\n",
    "\n",
    "input_str = \"给我讲一个年轻人奋斗创业最终取得成功的故事\"\n",
    "with torch.inference_mode():\n",
    "    prompt = QWEN_PROMPT_FORMAT.format(prompt=input_str)\n",
    "    input_ids = tokenizer.encode(prompt, return_tensors=\"pt\")\n",
    "    st = time.time()\n",
    "    output = model.generate(input_ids,\n",
    "                            max_new_tokens=512)\n",
    "    end = time.time()\n",
    "    output_str = tokenizer.decode(output[0], skip_special_tokens=True)\n",
    "    print(f'Inference time: {end-st} s')\n",
    "    print('-'*20, 'Prompt', '-'*20)\n",
    "    print(prompt)\n",
    "    print('-'*20, 'Output', '-'*20)\n",
    "    print(output_str)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## gradio-demo 体验\n",
    "\n",
    "为了得到更好的多轮对话体验，这里还提供了一个简单的 `gradio` demo界面方便调试使用，你可以修改内置 `system` 信息甚至微调模型让本地模型更接近你设想中的大模型需求。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2023-12-19 18:55:07,928 - WARNING - Warning: import flash_attn rotary fail, please install FlashAttention rotary to get higher efficiency https://github.com/Dao-AILab/flash-attention/tree/main/csrc/rotary\n",
      "2023-12-19 18:55:07,928 - WARNING - Warning: import flash_attn rms_norm fail, please install FlashAttention layer_norm to get higher efficiency https://github.com/Dao-AILab/flash-attention/tree/main/csrc/layer_norm\n",
      "2023-12-19 18:55:07,929 - WARNING - Warning: import flash_attn fail, please install FlashAttention to get higher efficiency https://github.com/Dao-AILab/flash-attention\n",
      "2023-12-19 18:55:08,065 - INFO - Converting the current model to sym_int4 format......\n",
      "2023-12-19 18:55:09,487 - INFO - HTTP Request: GET http://127.0.0.1:7862/startup-events \"HTTP/1.1 200 OK\"\n",
      "2023-12-19 18:55:09,496 - INFO - HTTP Request: HEAD http://127.0.0.1:7862/ \"HTTP/1.1 200 OK\"\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Running on local URL:  http://127.0.0.1:7862\n",
      "\n",
      "To create a public link, set `share=True` in `launch()`.\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div><iframe src=\"http://127.0.0.1:7862/\" width=\"100%\" height=\"500\" allow=\"autoplay; camera; microphone; clipboard-read; clipboard-write;\" frameborder=\"0\" allowfullscreen></iframe></div>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": []
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2023-12-19 18:55:10,170 - INFO - HTTP Request: GET https://checkip.amazonaws.com/ \"HTTP/1.1 200 OK\"\n",
      "2023-12-19 18:55:10,207 - INFO - HTTP Request: GET https://api.gradio.app/pkg-version \"HTTP/1.1 200 OK\"\n",
      "2023-12-19 18:55:10,296 - INFO - HTTP Request: GET https://checkip.amazonaws.com/ \"HTTP/1.1 200 OK\"\n",
      "2023-12-19 18:55:11,111 - INFO - HTTP Request: POST https://api.gradio.app/gradio-initiated-analytics/ \"HTTP/1.1 200 OK\"\n",
      "2023-12-19 18:55:11,246 - INFO - HTTP Request: POST https://api.gradio.app/gradio-launched-telemetry/ \"HTTP/1.1 200 OK\"\n"
     ]
    }
   ],
   "source": [
    "import gradio as gr\n",
    "import time\n",
    "from bigdl.llm.transformers import AutoModelForCausalLM\n",
    "from transformers import AutoTokenizer\n",
    "\n",
    "QWEN_PROMPT_FORMAT = \"<human>{prompt} <bot>\"\n",
    "\n",
    "load_path = \"qwen18chat_int4\"\n",
    "model = AutoModelForCausalLM.load_low_bit(load_path, trust_remote_code=True)\n",
    "tokenizer = AutoTokenizer.from_pretrained(load_path,trust_remote_code=True)\n",
    "\n",
    "def add_text(history, text):\n",
    "    _, history = model.chat(tokenizer, text, history=history)\n",
    "    return history, gr.Textbox(value=\"\", interactive=False)\n",
    "\n",
    "def bot(history):\n",
    "    response =  history[-1][1]\n",
    "    history[-1][1] = \"\"\n",
    "    for character in response:\n",
    "        history[-1][1] += character\n",
    "        time.sleep(0.05)\n",
    "        yield history\n",
    "\n",
    "with gr.Blocks() as demo:\n",
    "    chatbot = gr.Chatbot(\n",
    "        [], \n",
    "        elem_id=\"chatbot\",\n",
    "        bubble_full_width=False,\n",
    "    )\n",
    "\n",
    "    with gr.Row():\n",
    "        txt = gr.Textbox(\n",
    "            scale=4,\n",
    "            show_label=False,\n",
    "            placeholder=\"Enter text and press enter\",\n",
    "            container=False,\n",
    "        )\n",
    "\n",
    "    txt_msg = txt.submit(add_text, [chatbot, txt], [chatbot, txt], queue=False).then(\n",
    "        bot, chatbot, chatbot, api_name=\"bot_response\"\n",
    "    )\n",
    "    txt_msg.then(lambda: gr.Textbox(interactive=True), None, [txt], queue=False)\n",
    "\n",
    "demo.queue()\n",
    "demo.launch()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "利用 Intel 的大语言模型推理框架，我们可以实现大模型在 Intel 端侧设备的高性能推理。 只需要 2G 内存占用就可以实现与本地大模型的流畅对话，一起来体验下吧。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "bigdltest",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.18"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
